package com.atguigu.userprofile.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.userprofile.bean.TagCondition;
import com.atguigu.userprofile.bean.TagInfo;
import com.atguigu.userprofile.bean.UserGroup;
import com.atguigu.userprofile.constants.ConstCodes;
import com.atguigu.userprofile.mapper.UserGroupMapper;
import com.atguigu.userprofile.service.TagInfoService;
import com.atguigu.userprofile.service.UserGroupService;
import com.atguigu.userprofile.utils.RedisUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;

import org.apache.catalina.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhangchen
 * @since 2021-05-04
 */
@Service
@Slf4j
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {

    @Autowired
    TagInfoService tagInfoService;

    @Override
    public void saveUserGroupInfo(UserGroup userGroup) {
        //1 把分群信息保存到mysql中  // 借助mybatis-plus 现成的方法实现
        //1) 条件转json
        //2） 条件转中文描述
        //3) 创建日期
        List<TagCondition> tagConditions = userGroup.getTagConditions();
        //JSON -> 单个对象  parseObject JSON->对象集合  parseArray   对象->json toJSONString
        String conditionJson = JSON.toJSONString(tagConditions);
        userGroup.setConditionJsonStr(conditionJson);

        userGroup.setConditionComment(userGroup.conditionJsonToComment());
        userGroup.setCreateTime(new Date());

        saveOrUpdate(userGroup); //mybatis-plus会把自动生成的主键 回补到对象的id中

    }

    @Override
    public void genUserGroup(UserGroup userGroup) {
       //生成人群包 写入clickhouse
        insertUserGroupSQL(userGroup);
       // 把结果写入redis中
        saveUserGroupRedis(  userGroup );

    }

    //把人群包的用户uid集合 写入redis中
    public void saveUserGroupRedis(UserGroup userGroup ){
         //redis 中 不选择使用bitmap存储的原因？  1  数据会比较稀疏 且redis中不是roaringbitmap没有解决稀疏问题
                                         //  2  不涉及集合间运算
        //  type ?    set     key?   user_group:101   value?  uids ...    field/score ? 无    写api  ？  sadd    读取api ?   smembers    过期时间？  不过期

        //要做查询ck刚刚写入的分群人群包  sql  ck
        List<String> userGroupUids = baseMapper.getUserGroupUids(userGroup.getId().toString());
        //把list放入redis中
        Jedis jedis = RedisUtil.getJedis();
        String userGroupKey="user_group:"+userGroup.getId().toString();
        //list转数组
        String[] userGroupArr = userGroupUids.toArray(new String[]{});
        // 统一提交
        jedis.sadd(userGroupKey,userGroupArr);

        jedis.close();


    }



    //组合一条insert SQL
    public void insertUserGroupSQL(UserGroup userGroup){

        Map<String, TagInfo> tagInfoMapWithCode = tagInfoService.getTagInfoMapWithCode();

        String insertSQL="insert into user_group select '"+userGroup.getId()+"' ,"+getBitmapSQL(userGroup.getTagConditions(), tagInfoMapWithCode,userGroup.getBusiDate());

        System.out.println(insertSQL);

        baseMapper.insertUserGroup(insertSQL);


    }

   // List<TagCondition>
//select
//bitmapAnd(
//   bitmapAnd(
//   ( subsql1 )
//     ,
//   (subsql2  )
//  ),
//( subsql3 )
//)
    public String  getBitmapSQL(List<TagCondition> tagConditionList ,Map<String,TagInfo> tagInfoMap,String busiDate){
        StringBuilder bitmapSQL=new StringBuilder();
        for (TagCondition tagCondition : tagConditionList) {
            String bitmapSubSql = getBitmapSubSql(tagCondition, tagInfoMap,busiDate);
            if(bitmapSQL.length()==0){
                bitmapSQL.append(bitmapSubSql);
            }else{
                bitmapSQL.insert(0,"bitmapAnd(").append(",").append(bitmapSubSql).append(")");
            }
        }
        return bitmapSQL.toString();

    }


//    (select  groupBitmapMergeState(us)  from user_tag_value_string
//        where tag_code='TG_PERSONA_BASE_AGEGROUP'  and tag_value in ('80后','90后') and dt='2021-05-16'  )
//    TagCondition
//            一个子查询中的变量
// 1  tablename    需要知道 tagValueType     通过查询mysql    用tag_code   去查询出tag_info
//  2  tagcode 有条件中有
//  3  操作符 要用英文转义一下
//  4  tagvalue   1  数字 也可能是 字符串  字符串要加单引   同上 根据tag_info.tagValueType
//  5             2 个数 可能是1个 也可能是多个  多个用逗号拼接 加小括号   判断长度
    private String  getBitmapSubSql(TagCondition tagCondition ,Map<String,TagInfo> tagInfoMap ,String busiDate){
        String tagCode = tagCondition.getTagCode();
        TagInfo tagInfo = tagInfoMap.get(tagCode);
        String tableName =null;
        if(tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_STRING)){
            tableName="user_tag_value_string";
        }else if(tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_LONG)){
            tableName="user_tag_value_long";
        }else if(tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_DATE)){
            tableName="user_tag_value_date";
        }else if(tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)){
            tableName="user_tag_value_decimal";
        }
        String sqlOpt=getConditionOperator(  tagCondition.getOperator()); //转换操作符
        List<String> tagValues = tagCondition.getTagValues();
        //判断  数字
        String tagValue ="";
        if(tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_LONG)||tagInfo.getTagValueType().equals(ConstCodes.TAG_VALUE_TYPE_DECIMAL)){
            tagValue= StringUtils.join(tagValues,",");
        }else{
            tagValue="'" + StringUtils.join(tagValues, "','") + "'";
        }

        if(tagValues.size()>1){
            tagValue="("+tagValue+")";
        }

        String sql="(select  groupBitmapMergeState(us)  from "+tableName+" where tag_code='"+tagCode+"'  and tag_value "+sqlOpt+" "+tagValue +" and  dt='"+busiDate+"' )";

        return sql ;
    }



    // 把中文的操作代码转换为 判断符号
    private  String getConditionOperator(String operator){
        switch (operator){
            case "eq":
                return "=";
            case "lte":
                return "<=";
            case "gte":
                return ">=";
            case "lt":
                return "<";
            case "gt":
                return ">";
            case "neq":
                return "<>";
            case "in":
                return "in";
            case "nin":
                return "not in";
        }
        throw  new RuntimeException("操作符不正确");
    }

}
